<?php
// +----------------------------------------------------------------------
// | INPHP
// | Copyright (c) 2023 https://inphp.cc All rights reserved.
// | Author: 幺月儿(https://gitee.com/lulanyin) Email: inphp@qq.com
// | 该文件的开源协议以所在项目的LICENSE文件为准，请遵守开源协议要求
// +----------------------------------------------------------------------
// | 从zblog同步文章和分类数据
// +----------------------------------------------------------------------
namespace app\cms\cmd;

use app\cms\model\ArchiveModel;
use app\cms\model\CategoryModel;
use app\cms\model\TagsModel;
use Inphp\Core\Db\Db;
use Inphp\Core\Util\Str;

class syncZBlog
{
    /**
     * 指定导入到的站点ID
     * @var string
     */
    public string $siteId = "";

    /**
     * z-bloc安装时使用的表名前缀
     * @var string
     */
    public string $prefix = "zbp_";

    /**
     * 是否检测标签重复性
     * @var bool
     */
    public bool $checkTagExists = false;

    /**
     * 分类的新旧对应关系
     * @var array
     */
    public array $categories = [];

    /**
     * 标签的新旧对应关系
     * @var array
     */
    public array $tags = [];

    public string $lastIdFile = RUNTIME."/logs/cms/syncZBlog.txt";

    /**
     * 初始化
     * @param array $params
     */
    public function __construct(array $params = [])
    {
        //指定站点ID
        $siteId = $params["siteId"] ?? "";
        if (!empty($siteId)) {
            $sitesId = explode(",", $siteId);
            $sids = [];
            foreach ($sitesId as $sid) {
                if (is_numeric($sid) && $sid > 0 && !in_array(ceil($sid), $sids)) {
                    $sids[] = ceil($sid);
                }
            }
            $this->siteId = join(",", $sids);
        }
        $this->prefix = $params["prefix"] ?? "zbp_";
        $this->checkTagExists = isset($params["checkTag"]) && $params["checkTag"];
    }

    public function run()
    {
        //计算耗时
        $startTime = time();
        //先导入分类
        $this->syncCategory();
        //再导入所有标签
        $this->syncTags();
        //使用原生语句查询
        $last = is_file($this->lastIdFile) ? file_get_contents($this->lastIdFile) : '{"lastId":0, "time":0}';
        $last = @json_decode($last, true) ?? ["lastId" => 0, "time" => 0];
        $lastId = $last["lastId"] ?? 0;
        $sql = "select count(*) as num from `{$this->prefix}post` where log_ID>{$lastId}";
        if ($result = Db::query($sql)) {
            $rows = is_array($result) ? ($result[0]["num"] ?? 0) : 0;
            if ($rows > 0) {
                $sql = "select * from `{$this->prefix}post`";
                while (true) {
                    $list = Db::query($sql." where log_ID>{$lastId} order by log_ID asc limit 0,100");
                    if (empty($list)) {
                        break;
                    }
                    $insert = [];
                    foreach ($list as $item) {
                        //分类处理
                        $category = $this->categories["cate_{$item["log_CateID"]}"] ?? [];
                        //标签处理
                        $tags = $item["log_Tag"];
                        $tagsId = [];
                        if (!empty($tags)) {
                            $tags = explode("}{", substr($tags, 1, -1));
                            foreach ($tags as $tag) {
                                if (isset($this->tags["tag_{$tag}"])) {
                                    $tagsId[] = $this->tags["tag_{$tag}"];
                                }
                            }
                            $tagsId = array_unique($tagsId);
                        }
                        $mate = @unserialize($item["log_Mate"]) ?? [];
                        //
                        $createTime = date("Y-m-d H:i:s", $item["log_CreateTime"] ?? time());
                        $updateTime = date("Y-m-d H:i:s", $item["log_UpdateTime"] ?? time());
                        $insert[] = [
                            "siteId"        => !empty($this->siteId) ? $this->siteId : null,
                            "categoryId"    => $category["id"] ?? 0,
                            "categoriesId"  => !empty($category["parentsId"]) ? "{$category['id']},{$category['parentsId']}" : ($category['id'] ?? null),
                            "title"         => $item["log_Title"],
                            "detail"        => !empty($item["log_Content"]) ? encodeHtml($item["log_Content"]) : (!empty($item["log_Intro"]) ? encodeHtml($item["log_Intro"]) : null),
                            "tags"          => !empty($tagsId) ? join(",", $tagsId) : null,
                            "index"         => !empty($item["log_Alias"]) ? $item["log_Alias"] : null,
                            "top"           => $item["log_IsTop"] == 1 ? 1 : 0,
                            "keyword"       => $mate["editor_keyword"] ?? null,
                            "description"   => !empty($item["log_Intro"]) ? $item["log_Intro"] : ($mate["log_Intro"] ?? null),
                            "state"         => 1,
                            "time"          => $createTime,
                            "updateTime"    => $updateTime
                        ];
                        $lastId = $item["log_ID"];
                    }
                    $db = ArchiveModel::emptyQuery();
                    if (!$db->insert($insert)) {
                        echo "[同步文章失败]".PHP_EOL;
                        break;
                    } else {
                        echo "[已同步至]: {$lastId}".PHP_EOL;
                    }
                }
            }
        }
        $spanSeconds = time() - $startTime;
        $hours = floor($spanSeconds / 3600);
        $minutes = floor(($spanSeconds - $hours * 3600) / 60);
        $seconds = $spanSeconds - $hours * 3600 - $minutes * 60;
        //保存最后一个同步的ID
        if (!is_dir(RUNTIME."/logs/cms")) {
            @mkdir(RUNTIME."/logs/cms", 0777, true);
        }
        file_put_contents($this->lastIdFile, json_encode(["lastId" => $lastId, "time" => time()]));
        echo "同步结束，累计用时：{$hours}小时{$minutes}分{$seconds}秒".PHP_EOL;
    }

    /**
     * 同步分类
     * @param int $parentId
     * @param int $newParentId
     * @param null|string $newParentsId
     * @param int $newFloor
     */
    private function syncCategory(int $parentId = 0, int $newParentId = 0, null|string $newParentsId = null, int $newFloor = 0)
    {
        $sql = "select * from `{$this->prefix}category` where cate_ParentId={$parentId}";
        $list = Db::query($sql);
        foreach ($list as $item) {
            //检测分类是否存在
            $exists = CategoryModel::emptyQuery()
                ->where("parentId", $newParentId)
                ->where("name", $item["cate_Name"])
                ->first();
            if (!empty($exists)) {
                echo "[分类已存在，忽略]: {$item['cate_Name']}".PHP_EOL;
                //保存对应关系
                $this->categories["cate_".$item["cate_ID"]] = [
                    "id"        => $exists["id"],
                    "parentId"  => $newParentId,
                    "parentsId" => $exists["parentsId"],
                    "floor"     => $exists["floor"]
                ];
                continue;
            }
            $new = [
                "siteId"    => !empty($this->siteId) ? $this->siteId : null,
                "parentId"  => $newParentId,
                "parentsId" => $newParentsId,
                "floor"     => $newFloor,
                "name"      => $item["cate_Name"],
                "index"     => $item["cate_Alias"]
            ];
            $db = CategoryModel::emptyQuery();
            if ($db->insert($new)) {
                echo "[分类保存成功]: {$item['cate_Name']}".PHP_EOL;
                if ($newParentId > 0) {
                    CategoryModel::setHasChild($newParentId, 1);
                }
                $newId = $db->getLastInsertId();
                //保存对应关系
                $this->categories["cate_".$item["cate_ID"]] = [
                    "id"        => $newId,
                    "parentId"  => $newParentId,
                    "parentsId" => $newParentsId
                ];
                $this->syncCategory($item["cate_ID"], $newId, !empty($newParentsId) ? "{$newId},{$newParentsId}" : (string) $newId, $newFloor + 1);
            } else {
                echo "[分类保存失败]: {$item['cate_Name']}".PHP_EOL;
            }
        }
    }

    /**
     * 同步所有标签
     */
    private function syncTags()
    {
        $sql = "select * from `{$this->prefix}tag`";
        $list = Db::query($sql);
        foreach ($list as $item) {
            //检测标签是否重复
            $tag = !empty($item["tag_Name"]) ? Str::trim($item["tag_Name"]) : null;
            if (empty($tag)) {
                continue;
            }
            //如果需要检查标签的重复性
            if ($this->checkTagExists) {
                $exists = TagsModel::emptyQuery()->where("tag", $tag)->first();
                if (!empty($exists)) {
                    $this->tags["tag_{$item["tag_ID"]}"] = $exists["id"];
                    echo "[标签重复]: {$item['tag_Name']}".PHP_EOL;
                    continue;
                }
            }
            $db = TagsModel::emptyQuery();
            if ($db->insert(["tag" => $item["tag_Name"]])) {
                $newId = $db->getLastInsertId();
                $this->tags["tag_{$item["tag_ID"]}"] = $newId;
                echo "[标签保存成功]: {$item['tag_Name']}".PHP_EOL;
            } else {
                echo "[标签保存失败]: {$item['tag_Name']}".PHP_EOL;
            }
        }
    }
}